
package com.android.finaltracer.channel;

import java.util.List;
import java.util.Map;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;

import com.android.finaltracer.channel.SystemEventReceiver.networkStateCallback;
import com.android.finaltracer.model.DataFormatBasic;
import com.android.finaltracer.model.PositionInfo;
import com.android.finaltracer.model.PositionReport;
import com.android.finaltracer.model.Setting;
import com.android.finaltracer.servercall.RemoteCall;
import com.android.finaltracer.servercall.RemoteCallManager;
import com.android.finaltracer.servercall.SequenceHashmap;

public class Http extends Channel implements networkStateCallback {
    private static final String TAG = "Http";

    private RemoteCallManager mRCM = null;
    private ConnectivityManager mCm = null;
    private Context mContext;
    private MyHandler mHandler = null;
    private HandlerThread mThread = null;

    public Http(Context context, ChannelEventCallback cb) {
        super(cb);
        mContext = context;
        init(context);
    }

    public void init(Context context) {
        mRCM = RemoteCallManager.getInstance();
        mCm = (ConnectivityManager) mContext
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        // register for network state change
        SystemEventReceiver.registerForNetworkStateChange(context, this);
        // creating a working thread named "positionReportHandlerThread" and
        // start it
        if (mThread == null) {
            mThread = new HandlerThread("httpchannelthread");
        }
        mThread.start();
        // bind thread to handler, then handler can handle messages in the
        // message queue of the thread.
        if (mHandler == null) {
            mHandler = new MyHandler(mThread.getLooper());
        }
    }

    public void destroy() {
        if (mContext != null) {
            SystemEventReceiver.unRegisterForNetworkStateChange(mContext);
        }
        if (mThread != null) {
            mThread.stop();
        }
    }

    public boolean startRecvPositionReport(long delayMillis) {
        // check network state
        if (isNetworkOk() == false) {
            return false;
        }
        if (mHandler != null) {
            mHandler.startRecvingReport("all", delayMillis);
        }
        return true;
    }

    public void stopRecvPositionReport() {
        if (mHandler != null) {
            mHandler.stopRecvingReport();
        }
    }

    public boolean startHeartbeat(long delayMillis) {
        // check network state
        if (isNetworkOk() == false) {
            return false;
        }
        if (mHandler == null) {
            mHandler.startHeartbeat(delayMillis);
        }
        return true;
    }

    public void stopHeartbeat() {
        if (mHandler != null) {
            mHandler.stopHeartbeat();
        }
    }

    @Override
    public void send(String target, DataFormatBasic dataFormatBasic) {
        if (dataFormatBasic != null) {
            PositionInfo pi = (PositionInfo) dataFormatBasic;
            Setting setting = new Setting(mContext);
            String sender = setting.getSelfNumber();

            Map<String, Object> requestMap = new SequenceHashmap<String, Object>();
            requestMap.put("sender", sender);
            requestMap.put("receiver", target);
            requestMap.put("longitude", pi.getLongitude());
            requestMap.put("latitude", pi.getLatitude());
            mHandler.sendReprot(requestMap);

            // try {
            // mRCM.getDefaultRemoteCall().execute(RemoteCall.ACTION_UPDATE_GPS
            // , requestMap, null);
            // } catch (RemoteException e) {
            // // TODO Auto-generated catch block
            // e.printStackTrace();
            // }
        }
    }

    private void onReceiveRelativePosition(List<PositionReport> prList) {
        if (prList != null) {
            for (PositionReport pr : prList) {
                String from = pr.getSender();
                PositionInfo pi = new PositionInfo(pr.getLatitude(),
                        pr.getLongitude());
                mCallback.onReceive(from, pi);
            }
        }
    }

    private void onHeartbeatResult(boolean result) {
        mCallback.onHeartbeatResult(result);
    }

    @Override
    public void onNetworkStateChanged() {
        mIsAvailable = isNetworkOk();
        mCallback.onNetworkStateChanged(mIsAvailable);
    }

    private boolean isNetworkOk() {
        NetworkInfo activeNetInfo = mCm.getActiveNetworkInfo();
        if (activeNetInfo != null) {
            State state = activeNetInfo.getState();
            String name = activeNetInfo.getTypeName();
            boolean isAvailable = activeNetInfo.isAvailable();
            boolean isConnected = activeNetInfo.isConnected();
            Log.d(TAG, "receive " + state + " for " + name + ", available = "
                    + isAvailable + ", connected = " + isConnected);
            if (isAvailable == true && isConnected == true) {
                return true;
            }
        }
        return false;
    }

    class MyHandler extends Handler {
        public static final int EVENT_HEARTBEAT = 0;
        public static final int EVENT_SEND_REPORT = EVENT_HEARTBEAT + 1;
        public static final int EVENT_RECV_REPORT = EVENT_SEND_REPORT + 1;

        private boolean mRecvingReportEnabled = false;
        private long mRecvingReportDelay = 10 * 1000;

        private boolean mHeartbeatEnabled = false;
        private long mHeartbeatDelay = 5 * 1000;

        public MyHandler() {
        }

        public MyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {

                case EVENT_HEARTBEAT: {
                    if (mHeartbeatEnabled) {
                        // send heartbeat
                        onHeartbeatResult(sendHeartbeat());
                        // repeat
                        Message msg1 = obtainMessage(EVENT_HEARTBEAT);
                        sendMessageDelayed(msg1, mHeartbeatDelay);
                    }
                }
                    break;
                case EVENT_SEND_REPORT: {
                    Map<String, Object> requestMap = (Map<String, Object>) msg.obj;
                    try {
                        mRCM.getDefaultRemoteCall().execute(
                                RemoteCall.ACTION_UPDATE_GPS, requestMap, null);
                    } catch (RemoteException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                    break;

                case EVENT_RECV_REPORT: {
                    if (mRecvingReportEnabled) {
                        String sender = (String) msg.obj;
                        List<PositionReport> prList = getRelativePosition(sender);
                        onReceiveRelativePosition(prList);
                        // repeat
                        Message msg1 = obtainMessage(EVENT_RECV_REPORT);
                        msg1.obj = (String) msg.obj;
                        sendMessageDelayed(msg1, mRecvingReportDelay);
                    }
                }
                    break;

            }
        }

        public void sendReprot(Map<String, Object> requestMap) {
            Message msg = obtainMessage(EVENT_SEND_REPORT);
            msg.obj = requestMap;
            msg.sendToTarget();
        }

        public void startRecvingReport(String sender, long delayMillis) {
            mRecvingReportEnabled = true;
            mRecvingReportDelay = delayMillis;
            Message msg = obtainMessage(EVENT_RECV_REPORT);
            msg.obj = sender;
            msg.sendToTarget();
        }

        public void stopRecvingReport() {
            mRecvingReportEnabled = false;
        }

        /* FIXME */
        public void startHeartbeat(long delayMillis) {
            mHeartbeatEnabled = false;
            mHeartbeatDelay = delayMillis;
            Message msg = obtainMessage(EVENT_HEARTBEAT);
            msg.sendToTarget();
        }

        public void stopHeartbeat() {
            mHeartbeatEnabled = true;
        }
    }

    private List<PositionReport> getRelativePosition(String sender) {
        try {
            Setting setting = new Setting(mContext);
            String receiver = setting.getSelfNumber();

            Map<String, Object> requestMap = new SequenceHashmap<String, Object>();
            // requestMap.put("sender", sender);
            requestMap.put("receiver", receiver);

            List<PositionReport> result = mRCM.getDefaultRemoteCall()
                    .execute(RemoteCall.ACTION_GET_GPS, requestMap,
                            PositionReport.class);
            if (result != null) {
                return result;
            }
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    private boolean sendHeartbeat() {
        try {
            List result = mRCM.getDefaultRemoteCall().execute(
                    RemoteCall.ACTION_HEARTBEAT, null, null);
            if (result != null && result.size() == 1) {
                return ((Boolean) result.get(0) == true);
            }
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }
}
